Pelajari Durasi Temporal JavaScript, API modern untuk aritmetika, perbandingan, dan pemformatan interval waktu yang presisi. Belajarlah mengelola periode waktu secara global dengan percaya diri, menghindari jebakan umum objek Date.
Durasi Temporal JavaScript: Menguasai Aritmetika dan Pemformatan Interval Waktu untuk Aplikasi Global
Mengelola waktu dalam pengembangan perangkat lunak terkenal rumit. Mulai dari melacak linimasa proyek lintas benua hingga menjadwalkan konferensi video internasional, nuansa interval waktu, zona waktu, dan daylight saving time dapat dengan cepat menyebabkan bug yang halus namun kritis. Selama beberapa dekade, pengembang JavaScript telah bergelut dengan objek Date bawaan, sebuah alat yang, meskipun fungsional untuk titik waktu-tanggal sederhana, kurang memadai dalam hal aritmetika interval waktu yang presisi dan manajemen waktu yang kuat serta sadar global.
Memperkenalkan JavaScript Temporal API – proposal terobosan yang dirancang untuk menyediakan API modern, kuat, dan ramah pengguna untuk bekerja dengan tanggal dan waktu di JavaScript. Di antara tipe-tipe barunya yang kuat, Temporal.Duration menonjol sebagai solusi definitif untuk menangani interval waktu. Artikel ini akan membawa Anda menyelami Temporal.Duration, menjelajahi kemampuannya untuk aritmetika, perbandingan, dan pemformatan cerdas, memastikan aplikasi Anda dapat mengelola waktu dengan presisi dan kejelasan global.
Baik Anda sedang membangun sistem logistik global, platform perdagangan keuangan, atau perencana acara multi-zona waktu, memahami Temporal.Duration sangat penting untuk menghilangkan ambiguitas terkait waktu dan memberikan pengalaman pengguna yang andal dan terinternasionalisasi.
Kekurangan Objek Date JavaScript untuk Interval Waktu
Sebelum kita merayakan kedatangan Temporal.Duration, penting untuk memahami batasan objek Date yang ada, terutama saat menangani interval waktu. Objek Date merepresentasikan titik waktu tertentu, diukur dalam milidetik sejak epoch Unix (1 Januari 1970, UTC). Meskipun dapat digunakan untuk melakukan aritmetika dasar, ia membawa beberapa kelemahan bawaan yang membuatnya tidak cocok untuk manajemen durasi yang kuat:
-
Mutabilitas: Objek
Datebersifat mutable (dapat diubah). Setiap operasi pada objekDatemengubah status internalnya, yang dapat menyebabkan efek samping tak terduga dan bug yang sulit dilacak, terutama dalam aplikasi kompleks atau lingkungan konkuren.const d = new Date('2023-01-15T10:00:00Z'); const d2 = d; // d2 sekarang mereferensikan objek yang sama dengan d d.setHours(d.getHours() + 1); console.log(d.toISOString()); // 2023-01-15T11:00:00.000Z console.log(d2.toISOString()); // 2023-01-15T11:00:00.000Z (d2 juga berubah!) -
Kurangnya Konsep Durasi: Objek
Datetidak memiliki konsep langsung tentang "durasi" atau "periode". Menghitung selisih antara dua tanggal menghasilkan sejumlah milidetik, yang kemudian perlu dikonversi secara manual menjadi tahun, bulan, hari, dll. Konversi manual ini rentan terhadap kesalahan, terutama saat berhadapan dengan bulan dengan panjang bervariasi atau tahun kabisat.const date1 = new Date('2023-01-01T00:00:00Z'); const date2 = new Date('2023-03-01T00:00:00Z'); const diffMs = date2.getTime() - date1.getTime(); // Berapa bulan ini? Bagaimana dengan tahun kabisat? // (diffMs / (1000 * 60 * 60 * 24 * 30)) hanyalah perkiraan terbaik. console.log(`Perbedaan dalam milidetik: ${diffMs}`); console.log(`Perkiraan hari: ${diffMs / (1000 * 60 * 60 * 24)}`); // Berfungsi untuk hari, tetapi tidak kuat untuk bulan/tahun -
Ambiguitas Zona Waktu: Objek
Datesering kali mencampuradukkan waktu lokal dan UTC. Meskipun secara internal menyimpan milidetik UTC, metode-metodenya sering kali beroperasi pada zona waktu lokal sistem secara default, yang menyebabkan kebingungan dan inkonsistensi saat bekerja dengan sistem terdistribusi atau pengguna internasional. - Tantangan Daylight Saving Time (DST): Transisi DST dapat menyebabkan hari menjadi 23 atau 25 jam. Aritmetika sederhana (misalnya, menambahkan 24 jam ke suatu tanggal) mungkin tidak selalu menghasilkan hari kalender berikutnya, yang menyebabkan perhitungan yang salah ketika "hari" dianggap sebagai periode 24 jam yang tetap.
Batasan-batasan ini secara historis telah memaksa pengembang untuk mengandalkan pustaka pihak ketiga seperti Moment.js atau date-fns, atau menulis kode kustom yang kompleks dan rawan kesalahan untuk menangani interval waktu dengan benar. Temporal bertujuan untuk membawa kemampuan ini secara native ke JavaScript.
Memperkenalkan JavaScript Temporal: Pendekatan Modern terhadap Waktu
API Temporal adalah objek global baru yang komprehensif di JavaScript yang dirancang untuk menjadi pengganti modern bagi objek Date yang lama. Prinsip intinya adalah imutabilitas, penanganan zona waktu yang eksplisit, dan pemisahan urusan yang jelas antara konsep waktu yang berbeda. Temporal memperkenalkan beberapa kelas baru, masing-masing mewakili aspek waktu yang berbeda:
Temporal.Instant: Titik waktu spesifik yang tidak ambigu, independen dari kalender atau zona waktu mana pun (mirip dengan stempel waktu Unix, tetapi dengan presisi nanodetik).Temporal.ZonedDateTime: Titik waktu spesifik dalam kalender dan zona waktu tertentu. Ini adalah representasi paling lengkap dari tanggal dan waktu spesifik untuk pengguna.Temporal.PlainDate: Tanggal kalender (tahun, bulan, hari) tanpa waktu atau zona waktu.Temporal.PlainTime: Waktu jam dinding (jam, menit, detik, dll.) tanpa tanggal atau zona waktu.Temporal.PlainDateTime: Tanggal kalender dan waktu jam dinding bersama-sama, tanpa zona waktu.Temporal.PlainYearMonth: Tahun dan bulan spesifik dalam sistem kalender.Temporal.PlainMonthDay: Bulan dan hari spesifik dalam sistem kalender.Temporal.Duration: Panjang waktu yang bertanda, seperti "5 jam dan 30 menit" atau "2 hari". Inilah fokus kita untuk panduan ini.
Semua objek Temporal bersifat imutabel, artinya operasi seperti menambah atau mengurangi waktu akan membuat objek baru alih-alih memodifikasi yang sudah ada, sehingga meningkatkan prediktabilitas dan mengurangi bug.
Memahami Temporal.Duration
Temporal.Duration merepresentasikan panjang waktu. Yang terpenting, ia independen dari titik awal atau akhir tertentu. Ia hanyalah "berapa banyak waktu" yang telah berlalu atau akan berlalu. Ia dapat terdiri dari tahun, bulan, minggu, hari, jam, menit, detik, milidetik, mikrodetik, dan nanodetik. Setiap komponen adalah bilangan bulat, dan bisa positif atau negatif.
Misalnya, "2 jam dan 30 menit" adalah sebuah durasi. "Periode dari 1 Januari hingga 1 Maret" adalah durasi antara dua titik spesifik, yang dapat *diwakili* oleh Temporal.Duration, tetapi Duration itu sendiri hanyalah intervalnya.
Membuat Durasi
Ada beberapa cara mudah untuk membuat objek Temporal.Duration:
1. Menggunakan Constructor
Constructor memungkinkan Anda untuk menentukan setiap komponen secara langsung. Perhatikan bahwa argumen diurutkan dari unit terbesar (tahun) ke yang terkecil (nanodetik).
// new Temporal.Duration(tahun, bulan, minggu, hari, jam, menit, detik, milidetik, mikrodetik, nanodetik)
// Durasi 2 jam dan 30 menit
const duration1 = new Temporal.Duration(0, 0, 0, 0, 2, 30, 0, 0, 0, 0);
console.log(duration1.toString()); // P2H30M
// Durasi 1 tahun, 2 bulan, 3 hari
const duration2 = new Temporal.Duration(1, 2, 0, 3);
console.log(duration2.toString()); // P1Y2M3D
// Durasi -5 hari
const duration3 = new Temporal.Duration(0, 0, 0, -5);
console.log(duration3.toString()); // P-5D
2. Menggunakan Temporal.Duration.from() dengan Objek
Ini sering kali merupakan cara yang paling mudah dibaca untuk membuat durasi, memungkinkan Anda untuk hanya menentukan komponen yang Anda butuhkan.
// Durasi 1,5 jam
const halfHourDuration = Temporal.Duration.from({ hours: 1, minutes: 30 });
console.log(halfHourDuration.toString()); // P1H30M
// Durasi 7 hari (1 minggu)
const oneWeekDuration = Temporal.Duration.from({ days: 7 });
console.log(oneWeekDuration.toString()); // P7D
// Durasi dengan detik pecahan (misalnya, 2,5 detik)
const twoPointFiveSeconds = Temporal.Duration.from({ seconds: 2, milliseconds: 500 });
console.log(twoPointFiveSeconds.toString()); // PT2.5S
// Durasi negatif
const negativeDuration = Temporal.Duration.from({ minutes: -45 });
console.log(negativeDuration.toString()); // PT-45M
3. Menggunakan Temporal.Duration.from() dengan String ISO 8601
Temporal memanfaatkan format durasi ISO 8601, yang merupakan standar untuk merepresentasikan durasi. Ini sangat baik untuk mem-parsing durasi dari sumber data eksternal.
Formatnya secara umum terlihat seperti P[tahun]Y[bulan]M[minggu]W[hari]DT[jam]H[menit]M[detik]S. T memisahkan komponen tanggal dari komponen waktu.
// 1 tahun, 2 bulan, 3 hari
const isoDuration1 = Temporal.Duration.from('P1Y2M3D');
console.log(isoDuration1.toString()); // P1Y2M3D
// 4 jam, 5 menit, 6 detik
const isoDuration2 = Temporal.Duration.from('PT4H5M6S');
console.log(isoDuration2.toString()); // PT4H5M6S
// Durasi gabungan
const isoDuration3 = Temporal.Duration.from('P7DT12H30M');
console.log(isoDuration3.toString()); // P7DT12H30M
// Detik pecahan juga didukung
const isoDuration4 = Temporal.Duration.from('PT1.5S');
console.log(isoDuration4.toString()); // PT1.5S
Melakukan Aritmetika dengan Durasi
Kekuatan sejati dari Temporal.Duration bersinar dalam kemampuan aritmetikanya. Anda dapat menambah, mengurangi, mengalikan, dan membagi durasi, dan juga menambah/menguranginya dari tipe tanggal-waktu Temporal lainnya. Semua operasi mengembalikan objek Temporal.Duration baru karena imutabilitas.
Menambah Durasi
Metode add() menggabungkan dua durasi.
const sprintDuration = Temporal.Duration.from({ weeks: 2 });
const bufferDuration = Temporal.Duration.from({ days: 3 });
const totalProjectTime = sprintDuration.add(bufferDuration);
console.log(totalProjectTime.toString()); // P2W3D (atau P17D jika dinormalisasi nanti)
// Menambahkan durasi negatif sama dengan pengurangan
const result = Temporal.Duration.from({ hours: 5 }).add({ hours: -2 });
console.log(result.toString()); // PT3H
Anda juga dapat menambahkan durasi ke objek tanggal/waktu Temporal mana pun. Di sinilah keajaibannya terjadi, karena Temporal dengan benar menangani pergeseran zona waktu dan transisi DST jika relevan.
const projectStart = Temporal.PlainDateTime.from('2023-10-26T09:00:00');
const projectDuration = Temporal.Duration.from({ days: 10, hours: 4 });
const projectEnd = projectStart.add(projectDuration);
console.log(projectEnd.toString()); // 2023-11-05T13:00:00
// Dengan ZonedDateTime, aturan zona waktu diterapkan dengan benar
const meetingStartUTC = Temporal.ZonedDateTime.from('2024-03-09T14:00:00[UTC]');
const meetingDuration = Temporal.Duration.from({ hours: 1, minutes: 45 });
const meetingEndUTC = meetingStartUTC.add(meetingDuration);
console.log(meetingEndUTC.toString()); // 2024-03-09T15:45:00+00:00[UTC]
// Contoh melintasi batas DST (dengan asumsi 'Europe/Berlin' bergeser pada 03:00 tanggal 2024-03-31)
const springForwardStart = Temporal.ZonedDateTime.from('2024-03-30T22:00:00[Europe/Berlin]');
const twentyFourHours = Temporal.Duration.from({ hours: 24 });
const nextDay = springForwardStart.add(twentyFourHours); // Menambahkan 24 jam aktual
console.log(springForwardStart.toString()); // 2024-03-30T22:00:00+01:00[Europe/Berlin]
console.log(nextDay.toString()); // 2024-03-31T23:00:00+02:00[Europe/Berlin] (Waktu lokal melompati satu jam)
Perhatikan bagaimana menambahkan 24 jam ke 2024-03-30T22:00:00 di Berlin (yaitu UTC+1) menghasilkan 2024-03-31T23:00:00 (sekarang UTC+2). Jam melompat maju satu jam, sehingga waktu jam dinding satu jam lebih lambat pada tanggal yang sama relatif terhadap waktu jam dinding awal. Ini dengan tepat menunjukkan kesadaran Temporal terhadap zona waktu dan DST saat melakukan aritmetika pada `ZonedDateTime`.
Mengurangi Durasi
Metode subtract() bekerja mirip dengan add(), tetapi ia mengurangi waktu.
const deadlineDuration = Temporal.Duration.from({ days: 30 });
const gracePeriod = Temporal.Duration.from({ days: 5 });
const effectiveDeadline = deadlineDuration.subtract(gracePeriod);
console.log(effectiveDeadline.toString()); // P25D
const taskEnd = Temporal.PlainDateTime.from('2023-12-01T17:00:00');
const taskDuration = Temporal.Duration.from({ hours: 8, minutes: 30 });
const taskStart = taskEnd.subtract(taskDuration);
console.log(taskStart.toString()); // 2023-12-01T08:30:00
Mengalikan dan Membagi Durasi
Metode multiply() dan divide() menskalakan komponen durasi dengan faktor tertentu. Ini berguna untuk skenario seperti menghitung total waktu untuk beberapa iterasi sebuah tugas.
const trainingSession = Temporal.Duration.from({ minutes: 45 });
const weeklyTraining = trainingSession.multiply(5); // Lima sesi seminggu
console.log(weeklyTraining.toString()); // PT225M
const totalProjectHours = Temporal.Duration.from({ hours: 160 });
const teamMembers = 4;
const hoursPerMember = totalProjectHours.divide(teamMembers);
console.log(hoursPerMember.toString()); // PT40H
Menegasikan Durasi
Metode negate() membalikkan tanda semua komponen durasi. Durasi positif menjadi negatif, dan sebaliknya.
const delayDuration = Temporal.Duration.from({ hours: 3 });
const advanceDuration = delayDuration.negate();
console.log(delayDuration.toString()); // PT3H
console.log(advanceDuration.toString()); // PT-3H
Nilai Absolut Durasi
Metode abs() mengembalikan Temporal.Duration baru dengan semua komponen dibuat positif, secara efektif memberi Anda besaran durasi terlepas dari tandanya.
const negativeDelay = Temporal.Duration.from({ minutes: -60 });
const positiveDuration = negativeDelay.abs();
console.log(negativeDelay.toString()); // PT-60M
console.log(positiveDuration.toString()); // PT60M
Membandingkan dan Menormalisasi Durasi
Membandingkan durasi bisa jadi rumit, terutama ketika unit yang berbeda terlibat (misalnya, apakah 1 bulan sama dengan 30 hari?). Temporal menyediakan alat untuk perbandingan dan normalisasi guna menangani kompleksitas ini.
Membandingkan Durasi dengan compare()
Metode statis Temporal.Duration.compare(duration1, duration2, options) mengembalikan:
-1jikaduration1lebih kecil dariduration20jikaduration1sama denganduration21jikaduration1lebih besar dariduration2
Yang terpenting, saat membandingkan durasi yang mencakup unit dengan panjang variabel seperti tahun, bulan, atau minggu, Anda sering kali perlu menyediakan opsi relativeTo. Parameter ini adalah objek `Temporal.ZonedDateTime` atau `Temporal.PlainDateTime` yang menyediakan konteks tentang cara menafsirkan unit-unit ini (misalnya, berapa hari dalam bulan atau tahun tertentu).
const oneHour = Temporal.Duration.from({ hours: 1 });
const sixtyMinutes = Temporal.Duration.from({ minutes: 60 });
console.log(Temporal.Duration.compare(oneHour, sixtyMinutes)); // 0 (Keduanya setara)
const oneMonth = Temporal.Duration.from({ months: 1 });
const thirtyDays = Temporal.Duration.from({ days: 30 });
// Tanpa relativeTo, perbandingan bulan/tahun sulit dilakukan
console.log(Temporal.Duration.compare(oneMonth, thirtyDays)); // 0 (Temporal membuat tebakan yang wajar tanpa konteks, sering kali berdasarkan rata-rata)
// Dengan relativeTo, perbandingan menjadi presisi berdasarkan kalender konteks
const startOfJanuary = Temporal.PlainDate.from('2023-01-01');
const endOfFebruaryLeap = Temporal.PlainDate.from('2024-02-01'); // Tahun kabisat
// Pada Januari 2023, 1 bulan adalah 31 hari
const comparisonJan = Temporal.Duration.compare(oneMonth, thirtyDays, { relativeTo: startOfJanuary });
console.log(`1 bulan vs 30 hari pada Jan 2023: ${comparisonJan}`); // 1 (1 bulan > 30 hari)
// Pada Februari 2024 (tahun kabisat), 1 bulan adalah 29 hari
const comparisonFeb = Temporal.Duration.compare(oneMonth, thirtyDays, { relativeTo: endOfFebruaryLeap });
console.log(`1 bulan vs 30 hari pada Feb 2024: ${comparisonFeb}`); // -1 (1 bulan < 30 hari)
Menormalisasi Durasi dengan normalize() dan round()
Durasi dapat direpresentasikan dalam banyak cara (misalnya, 90 menit atau 1 jam 30 menit). Normalisasi dan pembulatan membantu menstandarkan representasi ini untuk konsistensi dan tampilan.
normalize()
Metode normalize() menyederhanakan komponen durasi jika memungkinkan (misalnya, 60 menit menjadi 1 jam, 24 jam menjadi 1 hari, asalkan konteks `relativeTo` memungkinkan jika melibatkan bulan/tahun).
const longMinutes = Temporal.Duration.from({ minutes: 90 });
console.log(longMinutes.toString()); // PT90M
console.log(longMinutes.normalize().toString()); // PT1H30M
const multipleDays = Temporal.Duration.from({ hours: 48 });
console.log(multipleDays.toString()); // PT48H
console.log(multipleDays.normalize().toString()); // P2D
round()
Metode round() lebih kuat untuk mengubah dan membulatkan durasi ke unit tertentu. Ia menerima objek opsi dengan:
largestUnit: Unit terbesar yang akan disertakan dalam output (misalnya, 'years', 'days', 'hours').smallestUnit: Unit terkecil yang akan disertakan dalam output (misalnya, 'minutes', 'seconds', 'milliseconds').roundingIncrement: Bilangan bulat yang digunakan untuk membulatkan unit terkecil (misalnya, 5 untuk membulatkan ke 5 menit terdekat).roundingMode: Cara menangani angka yang sama jaraknya (misalnya, 'halfExpand', 'trunc', 'ceil', 'floor').relativeTo: Diperlukan untuk membulatkan durasi yang mengandung tahun, bulan, atau minggu.
const complexDuration = Temporal.Duration.from({ hours: 2, minutes: 45, seconds: 30 });
// Bulatkan ke jam terdekat
const roundedToHours = complexDuration.round({ smallestUnit: 'hour' });
console.log(roundedToHours.toString()); // PT3H
// Bulatkan ke 30 menit terdekat, dengan tetap mempertahankan jam
const roundedTo30Minutes = complexDuration.round({
largestUnit: 'hour',
smallestUnit: 'minute',
roundingIncrement: 30
});
console.log(roundedTo30Minutes.toString()); // PT3H
const preciseDuration = Temporal.Duration.from({ minutes: 123, seconds: 45 });
// Tampilkan sebagai jam dan menit
const formattedDuration = preciseDuration.round({ largestUnit: 'hour', smallestUnit: 'minute' });
console.log(formattedDuration.toString()); // PT2H4M
// Pembulatan dengan bulan/tahun memerlukan relativeTo
const longTermDuration = Temporal.Duration.from({ months: 1, days: 10 });
const referenceDate = Temporal.PlainDate.from('2023-01-15');
// Bulatkan ke bulan, relatif terhadap suatu tanggal
const roundedToMonths = longTermDuration.round({ largestUnit: 'month', smallestUnit: 'month', relativeTo: referenceDate });
console.log(roundedToMonths.toString()); // P1M
Menghitung Durasi Antara Objek Temporal
Salah satu penggunaan durasi yang paling sering adalah menghitung interval waktu antara dua titik waktu spesifik. Temporal menyediakan metode until() dan since() pada objek tanggal-waktunya untuk tujuan ini.
Metode until()
Metode until() menghitung durasi dari objek penerima ke objek argumen. Ini inklusif pada titik awal dan eksklusif pada titik akhir. Ia menerima objek opsi yang mirip dengan round() untuk menentukan unit yang diinginkan dan perilaku pembulatan.
const startDate = Temporal.PlainDate.from('2023-01-01');
const endDate = Temporal.PlainDate.from('2023-03-15');
// Durasi dalam unit terbesar yang mungkin (bulan, lalu hari)
const projectLength = startDate.until(endDate);
console.log(projectLength.toString()); // P2M14D
// Durasi murni dalam hari
const totalDays = startDate.until(endDate, { largestUnit: 'day' });
console.log(totalDays.toString()); // P73D
// Durasi antara dua waktu spesifik, dengan memperhatikan zona waktu
const meetingStart = Temporal.ZonedDateTime.from('2024-07-20T10:00:00[America/New_York]');
const meetingEnd = Temporal.ZonedDateTime.from('2024-07-20T11:30:00[America/New_York]');
const elapsedMeetingTime = meetingStart.until(meetingEnd, { largestUnit: 'hour', smallestUnit: 'minute' });
console.log(elapsedMeetingTime.toString()); // PT1H30M
// Durasi lintas zona waktu (dari NYC ke London)
const nyStartTime = Temporal.ZonedDateTime.from('2024-08-01T09:00:00[America/New_York]');
const londonEndTime = Temporal.ZonedDateTime.from('2024-08-01T17:00:00[Europe/London]');
const travelDuration = nyStartTime.until(londonEndTime);
console.log(travelDuration.toString()); // PT13H (Waktu yang berlalu sebenarnya, bukan selisih jam dinding)
Contoh terakhir sangat mencerahkan. Meskipun New York 5 jam di belakang London, dan waktu jam dindingnya adalah jam 9 pagi dan 5 sore pada hari yang sama, metode until() dengan benar menghitung waktu yang berlalu sebenarnya yaitu 13 jam. Ini karena ZonedDateTime secara implisit menangani perbedaan zona waktu.
Metode since()
Metode since() adalah kebalikan dari until(). Ia menghitung durasi dari objek argumen ke objek penerima, menghasilkan durasi negatif jika argumen berada di masa depan relatif terhadap penerima.
const currentDateTime = Temporal.ZonedDateTime.from('2024-06-15T12:00:00[Europe/Paris]');
const historicEvent = Temporal.ZonedDateTime.from('2024-01-01T00:00:00[Europe/Paris]');
const timeSinceEvent = currentDateTime.since(historicEvent, { largestUnit: 'month', smallestUnit: 'day' });
console.log(timeSinceEvent.toString()); // P5M14D
const futureDate = Temporal.PlainDate.from('2025-01-01');
const pastDate = Temporal.PlainDate.from('2024-01-01');
const durationFromFuture = pastDate.since(futureDate);
console.log(durationFromFuture.toString()); // P-1Y
Menangani Unit yang Berbeda dan Pembulatan untuk Durasi yang Dihitung
Saat menghitung durasi, sering kali perlu untuk menentukan `largestUnit` dan `smallestUnit` untuk mendapatkan representasi yang dapat dibaca manusia, terutama untuk usia, waktu yang telah berlalu, atau hitung mundur.
const birthDate = Temporal.PlainDate.from('1990-07-15');
const today = Temporal.PlainDate.from('2024-06-15');
// Hitung usia dalam tahun, bulan, dan hari
const age = birthDate.until(today, { largestUnit: 'year', smallestUnit: 'day' });
console.log(`Usia: ${age.years} tahun, ${age.months} bulan, ${age.days} hari`); // Usia: 33 tahun, 11 bulan, 0 hari
// Hitung sisa waktu untuk tugas dalam jam dan menit
const now = Temporal.Instant.fromEpochSeconds(Date.now() / 1000);
const deadline = Temporal.Instant.from('2024-07-01T09:00:00Z');
const timeLeft = now.until(deadline, { largestUnit: 'hour', smallestUnit: 'minute', roundingMode: 'ceil' });
console.log(`Sisa waktu: ${timeLeft.hours} jam dan ${timeLeft.minutes} menit.`); // Contoh: Sisa waktu: 355 jam dan 38 menit.
Memformat Durasi untuk Audiens Global
Meskipun Temporal.Duration menyediakan representasi interval waktu yang presisi dan terprogram, ia tidak memiliki metode toLocaleString() bawaan. Ini disengaja: durasi adalah panjang waktu yang abstrak, dan tampilannya dapat sangat bervariasi tergantung pada konteks, lokal, dan tingkat detail yang diinginkan. Anda, sebagai pengembang, bertanggung jawab untuk menyajikan durasi dengan cara yang ramah pengguna dan dapat dipahami secara global.
Representasi String ISO 8601
Metode toString() default dari objek Temporal.Duration mengembalikan representasi string ISO 8601-nya. Ini sangat baik untuk komunikasi mesin-ke-mesin, serialisasi, dan penyimpanan, tetapi jarang untuk ditampilkan langsung kepada pengguna akhir.
const examDuration = Temporal.Duration.from({ hours: 2, minutes: 15 });
console.log(examDuration.toString()); // PT2H15M
const holidayDuration = Temporal.Duration.from({ weeks: 2, days: 3 });
console.log(holidayDuration.toString()); // P2W3D
Pemformatan Manual untuk Keterbacaan dan Internasionalisasi
Untuk tampilan yang dihadapi pengguna, Anda biasanya akan mengekstrak komponen durasi dan memformatnya menggunakan interpolasi string dan Intl API JavaScript.
Berikut adalah contoh fungsi kustom yang memformat durasi:
function formatDurationToHumanReadable(duration, locale = 'id-ID') {
const parts = [];
// Menggunakan Intl.NumberFormat untuk pemformatan angka yang sadar lokal
const numberFormatter = new Intl.NumberFormat(locale);
if (duration.years !== 0) {
parts.push(numberFormatter.format(duration.years) + ' ' + (duration.years === 1 ? 'tahun' : 'tahun'));
}
if (duration.months !== 0) {
parts.push(numberFormatter.format(duration.months) + ' ' + (duration.months === 1 ? 'bulan' : 'bulan'));
}
if (duration.weeks !== 0) {
parts.push(numberFormatter.format(duration.weeks) + ' ' + (duration.weeks === 1 ? 'minggu' : 'minggu'));
}
if (duration.days !== 0) {
parts.push(numberFormatter.format(duration.days) + ' ' + (duration.days === 1 ? 'hari' : 'hari'));
}
if (duration.hours !== 0) {
parts.push(numberFormatter.format(duration.hours) + ' ' + (duration.hours === 1 ? 'jam' : 'jam'));
}
if (duration.minutes !== 0) {
parts.push(numberFormatter.format(duration.minutes) + ' ' + (duration.minutes === 1 ? 'menit' : 'menit'));
}
if (duration.seconds !== 0) {
// Bulatkan detik untuk tampilan jika memiliki bagian pecahan
const roundedSeconds = numberFormatter.format(duration.seconds.toFixed(0)); // Atau toFixed(1) untuk satu desimal
parts.push(roundedSeconds + ' ' + (duration.seconds === 1 ? 'detik' : 'detik'));
}
if (parts.length === 0) {
// Tangani kasus di mana durasi nol atau sangat kecil (misalnya, hanya nanodetik)
if (duration.milliseconds !== 0 || duration.microseconds !== 0 || duration.nanoseconds !== 0) {
const totalMs = duration.milliseconds + duration.microseconds / 1000 + duration.nanoseconds / 1_000_000;
return numberFormatter.format(totalMs.toFixed(2)) + ' milidetik';
}
return '0 detik';
}
// Gabungkan bagian dengan koma dan 'dan' untuk bagian terakhir (penggabungan gaya Indonesia)
if (parts.length > 1) {
const lastPart = parts.pop();
return parts.join(', ') + ' dan ' + lastPart;
} else {
return parts[0];
}
}
const tripDuration = Temporal.Duration.from({ days: 3, hours: 10, minutes: 45 });
console.log(formatDurationToHumanReadable(tripDuration, 'id-ID')); // 3 hari, 10 jam dan 45 menit
console.log(formatDurationToHumanReadable(tripDuration, 'id-ID')); // 3 hari, 10 jam dan 45 menit (contoh Intl.NumberFormat)
const meetingReminder = Temporal.Duration.from({ minutes: 5 });
console.log(formatDurationToHumanReadable(meetingReminder, 'id-ID')); // 5 menit
const microDuration = Temporal.Duration.from({ nanoseconds: 1234567 });
console.log(formatDurationToHumanReadable(microDuration, 'id-ID')); // 1,23 milidetik
Untuk pluralisasi yang lebih canggih dan pemformatan daftar yang dilokalkan, Anda dapat menggabungkan ini dengan Intl.RelativeTimeFormat atau pustaka templat string yang lebih kompleks. Kuncinya adalah memisahkan perhitungan durasi (ditangani oleh Temporal) dari presentasinya (ditangani oleh logika pemformatan Anda dan Intl).
Menggunakan Intl.DurationFormat (Masa Depan/Proposal)
Ada proposal TC39 yang sedang berjalan untuk Intl.DurationFormat, yang bertujuan untuk menyediakan cara native yang sadar lokal untuk memformat durasi. Jika distandarisasi dan diimplementasikan, ini akan sangat menyederhanakan pemformatan manual yang ditunjukkan di atas, menawarkan solusi yang kuat untuk internasionalisasi langsung di dalam ekosistem JavaScript.
Kemungkinan akan bekerja mirip dengan objek Intl lainnya:
// Ini bersifat hipotetis dan berdasarkan keadaan proposal saat ini
// Periksa kompatibilitas browser sebelum digunakan dalam produksi
/*
const duration = Temporal.Duration.from({ days: 1, hours: 2, minutes: 30 });
const formatter = new Intl.DurationFormat('id-ID', {
style: 'long',
years: 'long',
days: 'long',
hours: 'long',
minutes: 'long',
});
console.log(formatter.format(duration)); // "1 hari, 2 jam, 30 menit"
const shortFormatter = new Intl.DurationFormat('id-ID', { style: 'short', hours: 'numeric', minutes: 'numeric' });
console.log(shortFormatter.format(duration)); // "1 h, 2 j, 30 mnt"
*/
Meskipun belum tersedia di semua lingkungan, pantau terus proposal ini karena menjanjikan solusi definitif untuk pemformatan durasi global di masa depan.
Kasus Penggunaan Praktis dan Pertimbangan Global
Temporal.Duration bukan hanya peningkatan akademis; ia memecahkan masalah dunia nyata dalam aplikasi yang beroperasi di berbagai zona waktu dan budaya.
1. Penjadwalan dan Manajemen Acara
Untuk platform yang mengelola acara internasional, konferensi, atau janji temu, menghitung durasi acara, waktu hingga acara dimulai, atau berapa lama waktu istirahat sangatlah penting. Temporal.Duration memastikan perhitungan ini akurat terlepas dari lokasi pengguna atau aturan zona waktu lokal.
// Hitung sisa waktu untuk webinar global
const now = Temporal.ZonedDateTime.from('2024-07-25T10:00:00[Europe/London]'); // Contoh waktu saat ini
const webinarStart = Temporal.ZonedDateTime.from('2024-07-26T14:30:00[Asia/Tokyo]');
const timeUntilWebinar = now.until(webinarStart, {
largestUnit: 'hour',
smallestUnit: 'minute',
roundingMode: 'ceil' // Bulatkan ke atas untuk memastikan pengguna tidak ketinggalan
});
console.log(`Webinar dimulai dalam ${timeUntilWebinar.hours} jam dan ${timeUntilWebinar.minutes} menit.`);
// Output akan akurat berdasarkan waktu aktual yang berlalu antara dua ZonedDateTime ini.
2. Metrik Kinerja dan Logging
Mengukur waktu eksekusi operasi, waktu respons API, atau durasi pekerjaan batch memerlukan presisi tinggi. Temporal.Duration, terutama bila dikombinasikan dengan Temporal.Instant (yang menawarkan presisi nanodetik), sangat ideal untuk ini.
const startTime = Temporal.Instant.now();
// Simulasikan operasi yang kompleks
for (let i = 0; i < 1_000_000; i++) { Math.sqrt(i); }
const endTime = Temporal.Instant.now();
const executionDuration = startTime.until(endTime);
// Format ke detik dengan milidetik untuk ditampilkan
const formattedExecution = executionDuration.round({ smallestUnit: 'millisecond', largestUnit: 'second' });
console.log(`Operasi memakan waktu ${formattedExecution.seconds}.${String(formattedExecution.milliseconds).padStart(3, '0')} detik.`);
3. Perhitungan Keuangan
Dalam keuangan, interval waktu yang presisi sangat penting untuk menghitung bunga, jangka waktu pinjaman, atau periode investasi. Jumlah hari, bulan, atau tahun yang tepat dalam suatu periode harus akurat, terutama saat berhadapan dengan tahun kabisat atau panjang bulan tertentu.
const loanStartDate = Temporal.PlainDate.from('2023-04-01');
const loanEndDate = Temporal.PlainDate.from('2028-03-31');
const loanTerm = loanStartDate.until(loanEndDate, { largestUnit: 'year', smallestUnit: 'month' });
console.log(`Jangka waktu pinjaman: ${loanTerm.years} tahun dan ${loanTerm.months} bulan.`); // 4 tahun dan 11 bulan
4. Tinjauan Ulang Tantangan Internasionalisasi
-
Zona Waktu dan DST:
Temporal.Durationitu sendiri agnostik terhadap zona waktu; ia mewakili panjang waktu yang tetap. Namun, ketika Anda menambah atau mengurangiDurationke/dariZonedDateTime, Temporal dengan benar menerapkan aturan zona waktu, termasuk pergeseran Daylight Saving Time. Ini memastikan bahwa "durasi 24 jam" benar-benar memajukanZonedDateTimesebanyak 24 jam aktual, bahkan jika itu berarti melintasi batas DST yang membuat hari *jam dinding* lebih pendek atau lebih panjang. Konsistensi ini adalah kemenangan besar atas `Date`. -
Variasi Budaya: Budaya yang berbeda mengekspresikan durasi secara berbeda. Meskipun
Temporal.Durationmenyediakan komponen mentah (tahun, bulan, hari, dll.), adalah tanggung jawab Anda untuk menggunakan API `Intl` atau logika kustom untuk menyajikannya dengan cara yang sesuai dengan lokal pengguna. Misalnya, beberapa budaya mungkin mengekspresikan "1 jam dan 30 menit" sebagai "90 menit" atau menggunakan aturan pluralisasi yang berbeda. -
Sistem Kalender: Temporal juga mendukung sistem kalender yang berbeda (misalnya, kalender Jepang, Persia, Islam). Meskipun
Durationitu sendiri agnostik terhadap kalender, ketika berinteraksi dengan tipe yang sadar kalender sepertiPlainDateatauZonedDateTime, aritmetikanya menghormati aturan kalender spesifik tersebut (misalnya, jumlah hari dalam sebulan, aturan tahun kabisat dalam kalender itu). Ini sangat penting untuk aplikasi global yang mungkin perlu menampilkan tanggal dalam kalender non-Gregorian.
Status dan Adopsi Temporal Saat Ini
Pada akhir 2023/awal 2024, JavaScript Temporal API adalah proposal TC39 Tahap 3. Ini berarti spesifikasinya sebagian besar sudah stabil, dan sedang dalam tahap implementasi dan pengujian di berbagai mesin dan peramban JavaScript. Peramban utama seperti Chrome, Firefox, dan Safari secara aktif sedang mengimplementasikan Temporal, dengan flag eksperimental atau versi awal yang sudah tersedia.
Meskipun belum tersedia secara universal tanpa polyfill, tahap lanjutnya menunjukkan bahwa kemungkinan besar akan menjadi bagian standar dari JavaScript. Pengembang dapat mulai bereksperimen dengan Temporal hari ini dengan menggunakan polyfill atau dengan mengaktifkan fitur eksperimental di lingkungan pengembangan peramban mereka. Adopsi awal memungkinkan Anda untuk lebih maju, memahami manfaatnya, dan mengintegrasikannya ke dalam proyek Anda seiring dengan peluncuran dukungan peramban.
Adopsi luasnya pada akhirnya akan secara signifikan mengurangi kebutuhan akan pustaka tanggal/waktu eksternal, menyediakan solusi native yang kuat untuk manajemen waktu JavaScript.
Praktik Terbaik Menggunakan Durasi Temporal
Untuk memaksimalkan manfaat Temporal.Duration dalam aplikasi Anda, pertimbangkan praktik terbaik berikut:
-
Utamakan
Temporal.Duration.from(): Saat membuat durasi dari komponen yang diketahui atau string ISO,Temporal.Duration.from()dengan objek literal seringkali lebih mudah dibaca dan tidak rentan kesalahan daripada argumen posisional constructor. -
Gunakan
relativeTountuk Perbandingan Ambigu: Selalu berikan opsirelativeTosaat membandingkan atau membulatkan durasi yang mengandung tahun, bulan, atau minggu. Ini memastikan perhitungan yang akurat dengan menyediakan konteks kalender yang diperlukan. -
Manfaatkan
until()dansince(): Untuk menghitung interval antara dua titik waktu spesifik, lebih baik gunakan metodeuntil()dansince()pada objek tanggal-waktu Temporal. Mereka menangani kompleksitas zona waktu dan DST dengan benar. -
Normalisasi dan Bulatkan untuk Tampilan: Sebelum menyajikan durasi kepada pengguna, pertimbangkan untuk menggunakan
normalize()dan terutamaround()untuk mengubah durasi menjadi unit yang paling sesuai dan dapat dipahami (misalnya, mengubah 90 menit menjadi "1 jam dan 30 menit"). -
Pisahkan Representasi Internal dari Tampilan: Jaga agar perhitungan durasi internal Anda tetap presisi dengan
Temporal.Duration. Hanya ubah dan format untuk tampilan saat me-render antarmuka pengguna, menggunakan fungsi pemformatan kustom dan APIIntluntuk akurasi global. - Tetap Terkini: Pantau terus kemajuan API Temporal dan kompatibilitas peramban. Saat bergerak menuju standardisasi penuh, ekosistem akan berkembang, dan alat atau praktik terbaik baru mungkin muncul.
- Perhatikan Presisi: Meskipun Temporal mendukung presisi nanodetik, gunakan hanya presisi yang benar-benar Anda butuhkan. Presisi yang lebih tinggi terkadang dapat membuat debugging lebih sulit atau menghasilkan pembulatan yang tidak terduga saat mengonversi ke tampilan dengan presisi lebih rendah.
Kesimpulan
Pengenalan Temporal.Duration menandai lompatan maju yang signifikan bagi pengembang JavaScript yang bergelut dengan aritmetika interval waktu. Dengan menyediakan API yang imutabel, eksplisit, dan sadar global, Temporal mengatasi keterbatasan lama dari objek Date yang usang.
Anda sekarang dapat dengan percaya diri melakukan perhitungan waktu yang kompleks, mengukur periode secara akurat, dan menyajikan durasi dengan cara yang presisi dan sesuai secara budaya untuk pengguna di seluruh dunia. Baik Anda menghitung durasi siklus rilis perangkat lunak, sisa waktu hingga peluncuran produk global, atau usia pasti seseorang, Temporal.Duration menawarkan alat yang Anda butuhkan untuk membangun aplikasi yang kuat dan andal.
Rangkullah Temporal.Duration dan ubah cara Anda mengelola waktu dalam proyek JavaScript Anda. Masa depan penanganan tanggal dan waktu di JavaScript telah tiba, menjanjikan pengalaman pengembangan yang lebih dapat diprediksi, kuat, dan kompatibel secara global.